# service for create task(eform) fro repair order
from django.db import connection
from backend.cews.services import LogService

from backend.cews.models import Parts, RepairOrder, RepairOrderPart, RepairOrderTask, Log ,Task,StockItemTask,StockItem
from backend.eform.models import EformSchema
from logger.createlogger import create_logger
from typing import Union
from django.db import transaction
import json

logger = create_logger('TaskService')

def create_task_master(task,description):
    """
        Create master of a task
    
        Args:
            task: string
            desc: string
    
        Returns:
            False|Task Model
    """
    
    try:
        taskModel = Task()
        taskModel.task = task
        taskModel.description = description
        taskModel.save()
    except Exception as e:
        logger.error(e)
        return False
    return task

def create_stock_item_task(
    task:Union[Task,int],
    stock_item:StockItem=None,
    stock_code=False,
    eform_schema:Union[EformSchema,int]=None
):
    """
        Create Stock item task
    
        Args:
            task: Task Model or Task ID
            stockItem: StockItem Model
            stock_code: StockItem stock_code
    
        Returns:
            False|StockItemTask Model
    
    """
    if not stock_item and not stock_code:
        raise ValueError('stockItem or stock_code must be provided')
    
    if stock_code:
        stock_item = StockItem.objects.get(stock_code=stock_code)
        
    ## check task instance if int query
    if not isinstance(task,Task):
        task = Task.objects.get(id=task)
    
    ## check eform_schema instance if model get the id
    
    eform_schema_id = None
    
    if isinstance(eform_schema,EformSchema):
        eform_schema_id = eform_schema.id
    else:
        eform_schema_id = eform_schema
    
    sit = StockItemTask()
    sit.stock_item = stock_item
    sit.name = task.task
    sit.eform_schema_id = eform_schema_id
    sit.save()
    
    return sit

def query_stock_item_tasks_by_stock_code(stock_code:str, mtce_type:str):
    """
        Get task by stock code and its children
    
        Args:
            stock_code: StockItem stock_code
    
        Returns:
            False|StockItemTask QuerySet
    """
    
    return StockItemTask.objects.select_related('stock_item').filter(stock_item__stock_code=stock_code,deleted=False,mtce_type=mtce_type)

def query_nested_stock_item_task_by_stock_code(stock_code:str):
    """
        Get task by stock code and its children
    
        Args:
            stock_code: StockItem stock_code
    
        Returns:
            False|StockItemTask QuerySet
    """
    stock_item = StockItem.objects.filter(stock_code=stock_code).first()
    stock_codes = []
    if stock_item:
        stock_codes.append(stock_item.stock_code)
        for relation in stock_item.children:
            stock_codes.append(relation.child.stock_code)
    
    return StockItemTask.objects.select_related('stock_item').filter(stock_item__stock_code__in=stock_codes)

def query_nested_stock_item_task_by_parts(parts:Parts):
    """
        Get task by part and its children
    
        Args:
            parts: Parts Model or Parts ID or Parts serial_no
    
        Returns:
            False|StockItemTask QuerySet
    """
    
    
    stock_codes = []
    
    stock_codes.append(parts.stock_code)
    
    if parts.is_parent:
        for child in parts.children:
            stock_codes.append(child.stock_code)
    
    return StockItemTask.objects.filter(stock_item__stock_code__in=stock_codes)



def create_repair_order_task(repair_order_part:Union[RepairOrderPart,int],stock_item_task:Union[StockItemTask,int],role:str):
    """
        create repair order task base on stock item task for a repair order
    
        Args:
            repair_order_part: id or model
            stock_item_task: id or model
            role: tester or checker
            
        Returns:
            False|RepairOrderTask Model

    """
    repair_order = RepairOrder.objects.get(id=repair_order_part.repair_order_id)

    if isinstance(repair_order_part,int):
        repair_order_part = RepairOrderPart.objects.get(id=repair_order_part)
    
    if not isinstance(stock_item_task,StockItemTask):
        stock_item_task = StockItemTask.objects.get(id=stock_item_task)
    
    rot = RepairOrderTask()
    
    rot.repair_order = repair_order_part.repair_order
    rot.repair_order_part = repair_order_part
    rot.stock_item_task = stock_item_task
    
    rot.stock_code = repair_order_part.stock_code
    rot.serial_no = repair_order_part.serial_no
    
    rot.role = role
    rot.eform_schema_id = stock_item_task.eform_schema_id

    rot.mtce_type = repair_order.mtce_type
    rot.final_tab = stock_item_task.final_tab
    rot.cur_tab = stock_item_task.cur_tab
    
    rot.save()
    
    return rot
    

def create_checker_tester_task_for_repair_order_part(repair_order_part:Union[RepairOrderPart,int],stock_item_task:Union[StockItemTask,int]):
    """
        create tester and checker repair order task for a repair order and its parts
    
        Args:
            repair_order_part: RepairOrder Model or RepairOrder ID
            stock_item_task: StockItemTask Model or StockItemTask ID
    
        Returns:
            list of created tasks
    """
    tester_task = None
    checker_task = None


    if isinstance(repair_order_part,int):
        repair_order_part = RepairOrderPart.objects.get(id=repair_order_part)
    
    if not isinstance(stock_item_task,StockItemTask):
        stock_item_task = StockItemTask.objects.get(id=stock_item_task)
    

    # check is task created
    
    if not RepairOrderTask.objects.filter(repair_order_part=repair_order_part,stock_item_task=stock_item_task,role=RepairOrderTask.RoleType.TESTER).exists():
        tester_task = create_repair_order_task(repair_order_part,stock_item_task,RepairOrderTask.RoleType.TESTER)
    
    if not RepairOrderTask.objects.filter(repair_order_part=repair_order_part,stock_item_task=stock_item_task,role=RepairOrderTask.RoleType.CHECKER).exists():
        checker_task = create_repair_order_task(repair_order_part,stock_item_task,RepairOrderTask.RoleType.CHECKER)
    
    return tester_task,checker_task

# def create_all_task_for_repair_order(repair_order:Union[RepairOrder,id]): ## should be depreciated
#     """
#         create tester and checker repair order task for a repair order and its parts
    
#         Args:
#             repair_order: RepairOrder Model or RepairOrder ID
    
#         Returns:
#             list of created tasks
#     """
#     if not isinstance(repair_order,RepairOrder):
#         repair_order = RepairOrder.objects.get(id=repair_order)
    
#     tasks = []
    
#     stock_item_tasks = query_nested_stock_item_task_by_parts(repair_order.part)
    
#     for sit in stock_item_tasks:
#         tester_task,checker_task = create_set_of_repair_order_task(repair_order,sit)
#         tasks.append(tester_task)
#         tasks.append(checker_task)
    
#     return tasks

def create_task_for_repair_order_part(repair_order_part:Union[RepairOrderPart,int]):
    """
        Create tester checker task for a repair order part
        1. query stock item task by stock_code
        2. create set of tasks (tester and checker)
        
        Args:
            repair_order_part: model or id
    
        Returns:
            list of created tasks
        
        Revision:
            2023-06-31: use only repair order part stock code to query stock item task
    """
    
    if isinstance(repair_order_part,int):
        repair_order_part = RepairOrderPart.objects.get(id=repair_order_part, deleted=False)
    
    repair_order = RepairOrder.objects.get(id=repair_order_part.repair_order_id)
    
    tasks = []
    
    # stock_item_tasks = query_nested_stock_item_task_by_parts(repair_order_part.part) filter by m'tce type
    stock_item_tasks = query_stock_item_tasks_by_stock_code(repair_order_part.stock_code, repair_order.mtce_type)
    
    for sit in stock_item_tasks:
        tester_task,checker_task = create_checker_tester_task_for_repair_order_part(repair_order_part,sit)
        
        if tester_task:
            tasks.append(tester_task)
            
        if checker_task:
            tasks.append(checker_task)
    
    return tasks

def query_repair_order_task_of_order(repair_order:Union[RepairOrder,int]):
    
    if not isinstance(repair_order,RepairOrder):
        repair_order = RepairOrder.objects.get(id=repair_order)

    return RepairOrderTask.objects.filter(repair_order=repair_order).select_related('stock_item_task','done_by','verify_by','endorse_by').order_by('id')
    

def filter_task_by_mtce_type(mtce_type):
    print(mtce_type)
    if mtce_type != '':
        tasks = Task.objects.filter(mtce_type=mtce_type)
    
    if mtce_type == '' or mtce_type is None:
        tasks = Task.objects.all()

    return tasks

def soft_delete_part(id, user):
    if not user.has_perm('cews.delete_task'):
        raise ValueError('User has no permission to delete task.')

    t = Task.objects.get(pk=id)
    # t.deleted = True
    # t.save()
    t.delete()

    return True


def create_order_task(order_id,name,mtce_type,cur_tab,final_tab,eform_schema_id,verify_user_id,endorse_user_id, request): 

    if not cur_tab and not final_tab:
        raise ValueError('Type of current task and final check should choose at least one')
    
    if not name:
        raise ValueError('Required task name')

    if not verify_user_id:
        verify_user_id = None
    
    if not endorse_user_id:
        endorse_user_id = None
    
    r = RepairOrder.objects.get(pk=order_id)
    repair_order_part = r.primary_part
    repair_order_part.status = r.status = RepairOrder.STATUS.IN_PROCESS
    with transaction.atomic():
        rot_tester = RepairOrderTask(
            repair_order=r,
            repair_order_part=repair_order_part,
            stock_code=repair_order_part.stock_code,
            serial_no=repair_order_part.serial_no,
            stock_item_task=None,
            name=name,
            mtce_type=mtce_type,
            cur_tab=cur_tab,
            final_tab=final_tab,
            eform_schema_id=eform_schema_id,
            role=RepairOrderTask.RoleType.TESTER,
            is_endorse_required=False,
        )
        rot_checker = RepairOrderTask(
            repair_order=r,
            repair_order_part=repair_order_part,
            stock_code=repair_order_part.stock_code,
            serial_no=repair_order_part.serial_no,
            stock_item_task=None,
            name=name,
            mtce_type=mtce_type,
            cur_tab=cur_tab,
            final_tab=final_tab,
            eform_schema_id=eform_schema_id,
            verify_user_id=verify_user_id,
            endorse_user_id=endorse_user_id,
            role=RepairOrderTask.RoleType.CHECKER,
            create_user_id=request.user.id
        )

        p = Parts.objects.get(deleted=False, stock_code=repair_order_part.stock_code, serial_no=repair_order_part.serial_no)

        result = LogService.check_part_is_parent(p)

        text = ''

        if cur_tab:
            text += 'current tab '

        if final_tab:
            text += 'final tab '

        new_log = Log(
            username=request.user.username,
            log_type=Log.LogType.MAINTENANCE,
            category_type=Log.CategoryType.TASK,
            action_type=Log.ActionType.CREATE,
            action="create_order_task",
            payload= json.dumps(request.data),
            description=f"{name} is created, display in {text}",
            repair_order=order_id,
            parent_stock_code=result["parent_stock_code"],
            parent_part=result["parent_serial_no"],
            part=result["serial_no"],
            stock_code=result["stock_code"]
        )

        new_log.save()
        rot_tester.save()
        rot_checker.save()
        r.save()
        repair_order_part.save()
    return True


def check_mtce_type_and_update_order_task(repair_order_id, mtce_type, request):
    r = RepairOrder.objects.get(pk=repair_order_id)

    if r.mtce_type == mtce_type:
        return 
    
    rots = RepairOrderTask.objects.filter(repair_order_id=repair_order_id)

    repair_order_part = rots.first().repair_order_part

    for rot in rots:
        if rot.done_by is not None: 
            raise ValueError(f"Task: {rot.stock_item_task.name} is already done by {rot.done_by}")
        if rot.verify_by is not None: 
            raise ValueError(f"Task: {rot.stock_item_task.name} is already verified by {rot.verify_by}")
        if rot.endorse_by is not None: 
            raise ValueError(f"Task: {rot.stock_item_task.name} is already endorsed by {rot.endorse_by}")
    
    ## delete repair order tasks
    for rot in rots:
        rot.delete()

    ## create new mtce type repair order tasks
    r.mtce_type = mtce_type
    r.save()

    create_task_for_repair_order_part(repair_order_part)

    result = LogService.check_part_is_parent(r.primary_part)

    new_log = Log(
        username=request.user,
        log_type=Log.LogType.MAINTENANCE,
        category_type=Log.CategoryType.ORDER,
        action_type=Log.ActionType.UPDATE,
        action='RepairOrderUpdateModelMixin',
        payload= json.dumps(request.data),
        description=f"Update order: {r.order_no}",
        repair_order=r.id,
        parent_stock_code=result['parent_stock_code'],
        parent_part=result['parent_serial_no'],
        part=result['serial_no'],
        stock_code=result['stock_code']
    )

    new_log.save()
